using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.Windows.Forms;
using ZoneFiveSoftware.Common.Data;
using ZoneFiveSoftware.Common.Data.Fitness;
using ZoneFiveSoftware.Common.Visuals;
using ZoneFiveSoftware.Common.Visuals.Chart;
using ZoneFiveSoftware.Common.Data.Measurement;
using FilteredStatistics.Controller;
using FilteredStatistics.Common;
using FilteredStatistics.Common.Controller;

namespace FilteredStatistics.View
{
    [DefaultEvent("ChartClick")]
    public partial class ActivityDetailsLineChart : UserControl
    {
        public enum XAxisValue
        {
            Time,
            Distance
        }

        public ActivityDetailsLineChart()
        {
            InitializeComponent();

            m_ActivityCallbackHelper = new ActivityDataChangedHelper(m_Activity);
            m_ActivityCallbackHelper.PropertyChanged += new PropertyChangedEventHandler(OnActivityDataChanged);

            MainChart.YAxis.SmartZoom = true;

            SaveImageButton.CenterImage = CommonResources.Images.Save16;
            ZoomToContentButton.CenterImage = Resources.Resources.ZoomToContent;
            ZoomInButton.CenterImage = CommonResources.Images.ZoomIn16;
            ZoomOutButton.CenterImage = CommonResources.Images.ZoomOut16;
            ExtraChartsButton.CenterImage = Resources.Resources.MoreCharts;
            ShowZonesButton.CenterImage = Resources.Resources.ShowZones;

            // Setup preset color array
            m_PresetOverlayColors.AddRange(new Color[] {
                Color.Red, Color.Crimson, Color.OrangeRed, Color.MediumVioletRed, Color.Fuchsia, Color.Tomato,
                Color.DarkGray, Color.DarkBlue, Color.Blue, Color.DodgerBlue, Color.MediumTurquoise, Color.CornflowerBlue,
                Color.Lime, Color.MediumAquamarine, Color.Olive, Color.DarkOliveGreen, Color.LightSeaGreen, Color.YellowGreen,
                Color.LightSalmon, Color.Magenta, Color.BlueViolet, Color.Green, Color.Khaki, Color.Gold });
        }

        void OnProviderDataTrackChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "DataTrack" ||
                e.PropertyName == "SmoothedDataTrack")
            {
                m_ResetDataSeries = true;
            }
            else if (e.PropertyName == "Axis")
            {
                m_ResetAxes = true;
            }
            else if (e.PropertyName == "AxisStripes")
            {
                m_ResetAxesStripes = true;
            }

            if (ActivityDetailExtension.Instance.ActivityDetailPage.Visible)
            {
                Invalidate();
            }
        }

        void OnActivityDataChanged(object sender, PropertyChangedEventArgs e)
        {
            bool dataChanged = false;

            if (e.PropertyName == "ElevationUnits")
            {
                m_ResetAxes = true;
                m_ResetDataSeries = true;
                dataChanged = true;
            }
            else if (e.PropertyName == "DistanceUnits")
            {
                if (XAxisReferential == XAxisValue.Distance)
                {
                    m_ResetAxes = true;
                    m_ResetDataSeries = true;
                    dataChanged = true;
                }
            }
            else if (e.PropertyName == "ActivityCategory.DistanceUnits")
            {
                if (XAxisReferential == XAxisValue.Distance)
                {
                    m_ResetAxes = true;
                    m_ResetDataSeries = true;
                    dataChanged = true;
                }
            }
            else if (e.PropertyName == "ActivityCategory.UseSystemLengthUnits")
            {
                if (XAxisReferential == XAxisValue.Distance)
                {
                    m_ResetAxes = true;
                    m_ResetDataSeries = true;
                    dataChanged = true;
                }
            }

            if (dataChanged && ActivityDetailExtension.Instance.ActivityDetailPage.Visible)
            {
                Invalidate();
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (!m_HoldPaint)
            {
                m_HoldPaint = true;
                {
                    if (m_ResetAxes)
                    {
                        m_ResetAxes = false;

                        SetupAxes();
                    }

                    if (m_ResetAxesStripes)
                    {
                        m_ResetAxesStripes = false;

                        UpdateAxisStripes();
                    }

                    if (m_ResetDataSeries)
                    {
                        m_ResetDataSeries = false;

                        if (Activity != null)
                        {
                            m_ActivityInfoCache = ActivityInfoCache.Instance.GetInfo(Activity);
                        }

                        SetupDataSeries();

                        if (Activity != null)
                        {
                            RefreshSelection();
                        }
                    }
                }
                m_HoldPaint = false;

                base.OnPaint(e);
            }
        }

#region UI callbacks

        private void MainChart_ChartClick(object sender, double xValueClicked, double yValueClicked)
        {
            if (ChartClick != null)
            {
                ChartClick(sender, xValueClicked, yValueClicked);
            }
        }

        private void MainChart_DataSerieClick(object sender, ChartDataSeries dataSerieClicked)
        {
            if (DataSerieClick != null)
            {
                DataSerieClick(sender, dataSerieClicked);
            }
        }

        private void SaveImageButton_Click(object sender, EventArgs e)
        {
            SaveImage dlg = new SaveImage();

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                Size imgSize = dlg.ImageSize;

                MainChart.SaveImage(imgSize, dlg.FileName, dlg.ImageFormat);
            }

            MainChart.Focus();
        }

        private void ZoomToContentButton_Click(object sender, EventArgs e)
        {
            MainChart.AutozoomToData(true);
            MainChart.Focus();
        }

        private void ZoomOutButton_Click(object sender, EventArgs e)
        {
            MainChart.ZoomOut();
            MainChart.Focus();
        }

        private void ZoomInButton_Click(object sender, EventArgs e)
        {
            MainChart.ZoomIn();
            MainChart.Focus();
        }

        private void ExtraChartsButton_Click(object sender, EventArgs e)
        {
            SelectChartsForm dlg = new SelectChartsForm(Options.Instance.SelectedExtraCharts, Activity);

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                Options.Instance.SelectedExtraCharts = dlg.SelectedCharts;

                SetupDataSeries();
                RefreshSelection();
            }
        }

        private void ShowZonesButton_Click(object sender, EventArgs e)
        {
            ContextMenu menu = new ContextMenu();
            MenuItem menuItem;

            // Add "None" to disable zones and seperator
            menuItem = new IconizedSTMenuItem(CommonResources.Text.LabelNone, new EventHandler(ZoneSelectedEventHandler));
            menuItem.Tag = Guid.Empty;
            if (m_ShownZones == Guid.Empty)
            {
                menuItem.Checked = true;
            }
            menu.MenuItems.Add(menuItem);
            menuItem = new IconizedSTMenuItem("-");
            menu.MenuItems.Add(menuItem);

            // Fill menu with the right list
            IActivityDataTrackProvider provider = ActivityDataTrackController.Instance.GetProvider(YAxisReferential);

            if (provider != null)
            {
                foreach (Guid zoneId in provider.DataTrackZoneGuids)
                {
                    string zoneName = provider.GetDataTrackZoneName(zoneId);
                    menuItem = new IconizedSTMenuItem(zoneName, new EventHandler(ZoneSelectedEventHandler));
                    if (m_ShownZones == zoneId)
                    {
                        menuItem.Checked = true;
                    }
                    menuItem.Tag = zoneId;
                    menu.MenuItems.Add(menuItem);
                }
            }

            menu.Show(this, PointToClient(MousePosition));
        }

        public void ZoneSelectedEventHandler(object sender, EventArgs args)
        {
            MenuItem clickedItem = sender as MenuItem;

            Options.Instance.ShowZonesInChart = clickedItem.Tag != null;
            ShownZones = (Guid)clickedItem.Tag;
        }

#endregion

        public void ThemeChanged(ITheme visualTheme)
        {
            MainChart.ThemeChanged(visualTheme);
            ButtonPanel.ThemeChanged(visualTheme);
            ButtonPanel.BackColor = visualTheme.Window;
        }

        public void UICultureChanged(CultureInfo culture)
        {
            m_ResetAxes = true;

            if (ActivityDetailExtension.Instance.ActivityDetailPage.Visible)
            {
                Invalidate();
            }
        }

        public void ZoomToData()
        {
            MainChart.AutozoomToData(true);
            MainChart.Refresh();
        }

        public string GetViewLabel()
        {
            string xAxisLabel = GetXAxisLabel(XAxisReferential);
            string yAxisLabel = GetShortYAxisLabel(YAxisReferential);

            return yAxisLabel + " / " + xAxisLabel;
        }

        public String GetXAxisLabel(XAxisValue axisReferential)
        {
            string xAxisLabel = String.Empty;

            switch (axisReferential)
            {
                case XAxisValue.Distance:
                    {
                        xAxisLabel = CommonResources.Text.LabelDistance;
                        break;
                    }
                case XAxisValue.Time:
                    {
                        xAxisLabel = CommonResources.Text.LabelTime;
                        break;
                    }
                default:
                    {
                        Debug.Assert(false);
                        break;
                    }
            }

            return xAxisLabel;
        }

        public String GetShortYAxisLabel(Guid chartId)
        {
            return GetShortYAxisLabel(chartId, Activity);
        }

        public static String GetShortYAxisLabel(Guid chartId, IActivity activity)
        {
            IActivityDataTrackProvider provider = ActivityDataTrackController.Instance.GetProvider(chartId);

            if (provider != null)
            {
                return provider.YAxisLabel;
            }

            return String.Empty;
        }

        public String GetYAxisLabel(Guid Guid)
        {
            return GetYAxisLabel(Guid, Activity);
        }

        public static String GetYAxisLabel(Guid chartId, IActivity activity)
        {
            string yAxisLabel = GetShortYAxisLabel(chartId, activity);
            IActivityDataTrackProvider provider = ActivityDataTrackController.Instance.GetProvider(chartId);

            if (provider != null)
            {
                string unitLabel = provider.YAxisUnitLabel;

                if (unitLabel != String.Empty)
                {
                    yAxisLabel += " " + unitLabel;
                }

                return yAxisLabel;
            }

            return String.Empty;
        }

        private void SetupDataSeries()
        {
            m_HoldPaint = true;
            {
                if (OverlayMode)
                {
                    SetupOverlayDataSeries();
                }
                else
                {
                    SetupRegularDataSeries();
                }

                ZoomToData();
            }
            m_HoldPaint = false;
        }

        private void SetupOverlayDataSeries()
        {
            MainChart.DataSeries.Clear();

            if (Activity != null)
            {
                float currentColorIndex = 0;

                m_ValidRanges.Sort(Utils.CompareDateTimeRangeAscending);

                foreach (ValueRange<DateTime> currentSegment in m_ValidRanges)
                {
                    ChartDataSeries selectionDataSeries = new ChartDataSeries(MainChart, MainChart.YAxis);

                    selectionDataSeries.ChartType = ChartDataSeries.Type.Line;
                    selectionDataSeries.FillColor = Color.Transparent;

                    if (FillSingleDataSerie(YAxisReferential, selectionDataSeries,
                                            currentSegment.Lower, currentSegment.Upper))
                    {
                        MainChart.DataSeries.Add(selectionDataSeries);

                        if (m_Selection.Contains(currentSegment))
                        {
                            selectionDataSeries.LineColor = m_PresetOverlayColors[(int)currentColorIndex % m_PresetOverlayColors.Count];
                            selectionDataSeries.LineWidth = 3;
                        }
                        else
                        {
                            selectionDataSeries.LineColor = Color.FromArgb(128, ChartValidRegionColor);
                            selectionDataSeries.LineWidth = 2;
                        }
                    }

                    currentColorIndex += (float)Math.Max(1, (float)m_PresetOverlayColors.Count / m_ValidRanges.Count);
                }
            }
        }

        private void SetupRegularDataSeries()
        {
            m_MainDataSerieCopy = new ChartDataSeries(MainChart, MainChart.YAxis);
            m_MainDataSerie = new ChartDataSeries(MainChart, MainChart.YAxis);
            m_SelectedDataSerie = new ChartDataSeries(MainChart, MainChart.YAxis);

            // Add main data.  We must use 2 separate data series to overcome the display
            //  bug in fill mode.  The main data series is normally rendered but the copy
            //  is set in Line mode to be displayed over the fill.
            //  The main is used for valid regions and third one is used for the selection.
            MainChart.DataSeries.Clear();

            if (Activity != null)
            {
                // Setup main chart
                MainChart.DataSeries.Add(m_MainDataSerie);
                MainChart.DataSeries.Add(m_SelectedDataSerie);
                MainChart.DataSeries.Add(m_MainDataSerieCopy);

                m_MainDataSerie.ChartType = ChartDataSeries.Type.Fill;
                m_MainDataSerie.FillColor = ChartFillColor;
                m_MainDataSerie.LineColor = ChartLineColor;
                m_MainDataSerie.SelectedColor = ChartValidRegionColor;

                m_SelectedDataSerie.ChartType = ChartDataSeries.Type.Fill;
                m_SelectedDataSerie.FillColor = Color.Transparent;
                m_SelectedDataSerie.LineColor = Color.Transparent;
                m_SelectedDataSerie.SelectedColor = ChartSelectedColor;

                m_MainDataSerieCopy.ChartType = ChartDataSeries.Type.Line;
                m_MainDataSerieCopy.LineColor = ChartLineColor;
                m_MainDataSerieCopy.SelectedColor = ChartSelectedColor;

                FillSingleDataSerie(YAxisReferential, m_MainDataSerie);
                FillSingleDataSerie(YAxisReferential, m_SelectedDataSerie);
                FillSingleDataSerie(YAxisReferential, m_MainDataSerieCopy);

                // Setup secondary charts
                MainChart.YAxisRight.Clear();

                foreach (IActivityDataTrackProvider provider in m_ExtraChartProviders)
                {
                    provider.DataTrackChanged -= new PropertyChangedEventHandler(OnProviderDataTrackChanged);
                }
                m_ExtraChartProviders.Clear();

                foreach (Guid chartId in Options.Instance.SelectedExtraCharts)
                {
                    IActivityDataTrackProvider provider = ActivityDataTrackController.Instance.GetProvider(chartId);

                    if (provider != null)
                    {
                        IAxis newAxis = new RightVerticalAxis(MainChart);
                        ChartDataSeries secondaryData = new ChartDataSeries(MainChart, newAxis);

                        // Only add if the track if available
                        if (FillSingleDataSerie(chartId, secondaryData))
                        {
                            m_ExtraChartProviders.Add(provider);
                            provider.DataTrackChanged += new PropertyChangedEventHandler(OnProviderDataTrackChanged);

                            MainChart.YAxisRight.Add(newAxis);
                            MainChart.DataSeries.Add(secondaryData);

                            SetupYAxisFromType(newAxis, chartId);
                            SetupYAxisAndDataColorFromType(newAxis, secondaryData, chartId);
                        }
                    }
                }
            }
        }

        private bool FillSingleDataSerie(Guid chartId, ChartDataSeries dataSerie)
        {
            return FillSingleDataSerie(chartId, dataSerie,
                                       m_ActivityInfoCache.ActualTrackStart,
                                       m_ActivityInfoCache.ActualTrackEnd);
        }

        private bool FillSingleDataSerie(Guid chartId, ChartDataSeries dataSerie,
                                         DateTime startTime, DateTime endTime)
        {
            INumericTimeDataSeries graphPoints = GetSmoothedActivityTrack(chartId);
            graphPoints = Utils.RemovePausedTimesInTrack(graphPoints, Activity);

            if (dataSerie != null &&
                graphPoints != null && graphPoints.Count > 0)
            {
                float startSecondOffset = (float)(startTime - graphPoints.StartTime).TotalSeconds;
                float endSecondOffset = (float)(endTime - graphPoints.StartTime).TotalSeconds;

                if (XAxisReferential == XAxisValue.Time)
                {
                    foreach (ITimeValueEntry<float> entry in graphPoints)
                    {
                        float newKey = entry.ElapsedSeconds - startSecondOffset;

                        Debug.Assert(!dataSerie.Points.ContainsKey(newKey));

                        if (entry.ElapsedSeconds >= startSecondOffset &&
                            entry.ElapsedSeconds <= endSecondOffset &&
                            !dataSerie.Points.ContainsKey(newKey))
                        {
                            dataSerie.Points.Add(newKey,
                                                 new PointF(entry.ElapsedSeconds - startSecondOffset, entry.Value));
                        }
                    }
                }
                else if (m_ActivityInfoCache.MovingDistanceMetersTrack != null)
                {
                    INumericTimeDataSeries distanceTrack = Utils.RemovePausedTimesInTrack(m_ActivityInfoCache.MovingDistanceMetersTrack, Activity);

                    if (distanceTrack != null)
                    {
                        int pointCount = Math.Min(distanceTrack.Count, graphPoints.Count);
                        ITimeValueEntry<float> startEntry = distanceTrack.GetInterpolatedValue(graphPoints.StartTime);

                        foreach (ITimeValueEntry<float> entry in graphPoints)
                        {
                            if (entry.ElapsedSeconds >= startSecondOffset &&
                                entry.ElapsedSeconds <= endSecondOffset)
                            {
                                ITimeValueEntry<float> interpolatedEntry = distanceTrack.GetInterpolatedValue(graphPoints.StartTime + new TimeSpan(0, 0, (int)entry.ElapsedSeconds));

                                if (interpolatedEntry != null)
                                {
                                    float distanceAtTime = interpolatedEntry.Value;
                                    float distanceValue = (float)Length.Convert(distanceAtTime - startEntry.Value, Length.Units.Meter, Activity.Category.DistanceUnits);

                                    dataSerie.Points.Add(entry.ElapsedSeconds - startSecondOffset,
                                                         new PointF(distanceValue, entry.Value));
                                }
                            }
                        }
                    }
                }

                return true;
            }

            return false;
        }

        private void SetupAxes()
        {
            // X axis
            switch (XAxisReferential)
            {
                case XAxisValue.Distance:
                    {
                        if (PluginMain.GetApplication() != null)
                        {
                            Length.Units distanceUnit = PluginMain.GetApplication().SystemPreferences.DistanceUnits;

                            if (Activity != null)
                            {
                                distanceUnit = Activity.Category.DistanceUnits;
                            }

                            MainChart.XAxis.Formatter = new Formatter.General(2);
                            MainChart.XAxis.Label = CommonResources.Text.LabelDistance + " (" +
                                                    Length.Label(distanceUnit) + ")";
                        }
                        break;
                    }
                case XAxisValue.Time:
                    {
                        MainChart.XAxis.Formatter = new Formatter.SecondsToTime();
                        MainChart.XAxis.Label = CommonResources.Text.LabelTime;
                        break;
                    }
                default:
                    {
                        Debug.Assert(false);
                        break;
                    }
            }

            // Y axis
            SetupYAxisFromType(MainChart.YAxis, YAxisReferential);
            SetupAxisStripes(MainChart.YAxis, YAxisReferential);

            if (!OverlayMode)
            {
                if (m_ExtraChartProviders.Count == MainChart.YAxisRight.Count)
                {
                    for (int i = 0; i < m_ExtraChartProviders.Count; ++i)
                    {
                        Guid chartId = m_ExtraChartProviders[i].ReferenceId;
                        IAxis axis = MainChart.YAxisRight[i];

                        if (axis != null)
                        {
                            SetupYAxisFromType(axis, chartId);
                        }
                    }
                }
                else
                {
                    SetupDataSeries();
                }
            }
            else
            {
                MainChart.YAxisRight.Clear();
            }
        }

        private void SetupYAxisFromType(IAxis yAxis, Guid chartId)
        {
            IActivityDataTrackProvider provider = ActivityDataTrackController.Instance.GetProvider(chartId);

            if (provider != null)
            {
                yAxis.Label = GetYAxisLabel(chartId, Activity);
                yAxis.Formatter = provider.AxisFormatter;
            }
        }

        private void UpdateAxisStripes()
        {
            if (Activity != null && Options.Instance.ShowZonesInChart)
            {
                IActivityDataTrackProvider provider = ActivityDataTrackController.Instance.GetProvider(YAxisReferential);

                ShownZones = provider.DefaultZone;
                if (MainChart != null)
                {
                    SetupAxisStripes(MainChart.YAxis, YAxisReferential);
                }
            }
        }

        private void SetupAxisStripes(IAxis yAxis, Guid chartId)
        {
            if (yAxis != null && Activity != null)
            {
                yAxis.Stripes.Clear();

                if (Options.Instance.ShowZonesInChart && ShownZones != null)
                {
                    IActivityDataTrackProvider provider = ActivityDataTrackController.Instance.GetProvider(YAxisReferential);

                    IList<AxisStripe> stripes = provider.GetDataTrackZones(ShownZones);

                    if (stripes != null)
                    {
                        foreach (AxisStripe stripe in stripes)
                        {
                            yAxis.Stripes.Add(stripe);

                            // Setup default parameters
                            stripe.Color = Color.FromArgb(16, 0, 0, 0);
                            stripe.LineColor = Color.FromArgb(128, 0, 0, 0);
                            stripe.LineStyle = DashStyle.Dash;
                            stripe.LineWidth = 2;
                        }
                    }

                    if (MainChart != null)
                    {
                        MainChart.Invalidate();
                    }
                }
            }
        }

        private void SetupYAxisAndDataColorFromType(IAxis yAxis, ChartDataSeries dataSerie, Guid chartId)
        {
            m_HoldPaint = true;
            IActivityDataTrackProvider provider = ActivityDataTrackController.Instance.GetProvider(chartId);

            yAxis.LabelColor = provider.TrackColor;
            dataSerie.LineColor = provider.TrackColor;
            m_HoldPaint = false;
        }

        private INumericTimeDataSeries GetSmoothedActivityTrack(Guid chartId)
        {
            IActivityDataTrackProvider provider = ActivityDataTrackController.Instance.GetProvider(chartId);

            if (provider.SmoothedDataTrack != null)
            {
                return provider.SmoothedDataTrack;
            }
            else
            {
                return new NumericTimeDataSeries();
            }
        }

        private ValueRange<float> RemapRangeToXAxis(ValueRange<DateTime> range)
        {
            float lower = 0;
            float upper = 0;

            if (Activity != null && m_ActivityInfoCache != null && range != null)
            {
                if (XAxisReferential == XAxisValue.Time)
                {
                    lower = (float)(range.Lower - m_ActivityInfoCache.ActualTrackStart).TotalSeconds;
                    upper = (float)(range.Upper - m_ActivityInfoCache.ActualTrackStart).TotalSeconds;
                }
                else if (m_ActivityInfoCache.MovingDistanceMetersTrack != null)
                {
                    INumericTimeDataSeries distanceTrack = Utils.RemovePausedTimesInTrack(m_ActivityInfoCache.MovingDistanceMetersTrack, Activity);

                    if (distanceTrack.Count > 0)
                    {
                        ITimeValueEntry<float> lowerInterpolatedValue = distanceTrack.GetInterpolatedValue(range.Lower);
                        ITimeValueEntry<float> upperInterpolatedValue = distanceTrack.GetInterpolatedValue(range.Upper);

                        // Fix the values for mismatched tracks
                        if (lowerInterpolatedValue == null)
                        {
                            lowerInterpolatedValue = distanceTrack[0];
                        }
                        if (upperInterpolatedValue == null)
                        {
                            upperInterpolatedValue = distanceTrack[distanceTrack.Count - 1];
                        }

                        if (lowerInterpolatedValue != null && upperInterpolatedValue != null)
                        {
                            lower = (float)Length.Convert(lowerInterpolatedValue.Value, Length.Units.Meter, Activity.Category.DistanceUnits);
                            upper = (float)Length.Convert(upperInterpolatedValue.Value, Length.Units.Meter, Activity.Category.DistanceUnits);
                        }
                    }
                }
            }

            return new ValueRange<float>(lower, upper);
        }

        private bool IsDataSerieDisplayed(Guid ChartId)
        {
            if (Options.Instance.ChartYAxis == ChartId ||
                Options.Instance.SelectedExtraCharts.Contains(ChartId))
            {
                return true;
            }

            return false;
        }

        public bool IsInFillRegion(double xValue, double yValue)
        {
            if (m_MainDataSerie != null)
            {
                float floatXValue = (float)xValue;
                double chartYValue = m_MainDataSerie.GetYValueAtX(ref floatXValue);

                if (yValue >= 0)
                {
                    return yValue >= 0 && yValue <= chartYValue;
                }
                else
                {
                    return yValue <= 0 && yValue >= chartYValue;
                }
            }

            return false;
        }

        public void AddSelectedRange(ValueRange<DateTime> range)
        {
            // If we already have 1 selection, clear it because we will
            //  need to highlight regions instead of a range
            if (m_Selection.Count == 1)
            {
                ClearSelectedRangeInternal();

                AddSelectedRangeInternal(m_Selection[0]);
            }

            m_Selection.Add(range);

            if (m_Selection.Count == 1)
            {
                // 1 selection, use SelectedRange
                SetSelectedRangeInternal(m_Selection[0]);
            }
            else
            {
                //  Multiple selection, use SelectedRegion
                AddSelectedRangeInternal(range);
            }

            if(OverlayMode)
            {
                RefreshSelection();
            }
        }

        private void AddSelectedRangeInternal(ValueRange<DateTime> range)
        {
            ValueRange<float> remappedRange = RemapRangeToXAxis(range);

            AddSelectedRangeInternal(remappedRange);
        }

        private void AddSelectedRangeInternal(ValueRange<float> range)
        {
            foreach (ChartDataSeries dataSerie in MainChart.DataSeries)
            {
                if (dataSerie != m_MainDataSerie)
                {
                    dataSerie.AddSelecedRegion(range.Lower, range.Upper);
                }
            }

            MainChart.Invalidate();
        }

        public void ClearValidRanges()
        {
            if (OverlayMode)
            {
                m_ResetDataSeries = true;
                MainChart.DataSeries.Clear();
            }

            m_ValidRanges.Clear();

            ClearValidRangesInternal();
        }

        public void ClearValidRangesInternal()
        {
            if(m_MainDataSerie != null)
            {
                m_MainDataSerie.ClearSelectedRegions();
            }
        }

        public void AddValidRange(ValueRange<DateTime> range)
        {
            if (OverlayMode)
            {
                m_ResetDataSeries = true;
                MainChart.DataSeries.Clear();
            }

            m_ValidRanges.Add(range);

            ValueRange<float> remappedRange = RemapRangeToXAxis(range);

            AddValidRangeInternal(remappedRange);
        }

        private void AddValidRangeInternal(ValueRange<DateTime> range)
        {
            ValueRange<float> remappedRange = RemapRangeToXAxis(range);

            AddValidRangeInternal(remappedRange);
        }

        private void AddValidRangeInternal(ValueRange<float> range)
        {
            if (m_MainDataSerie != null)
            {
                m_MainDataSerie.AddSelecedRegion(range.Lower, range.Upper);

                MainChart.Invalidate();
            }
        }

        public void ClearSelectedRange()
        {
            m_Selection.Clear();

            ClearSelectedRangeInternal();

            if (OverlayMode)
            {
                RefreshSelection();
            }
        }

        private void ClearSelectedRangeInternal()
        {
            m_HoldPaint = true;
            {
                foreach (ChartDataSeries dataSerie in MainChart.DataSeries)
                {
                    if (dataSerie != m_MainDataSerie)
                    {
                        dataSerie.ClearSelectedRegions();
                        dataSerie.SetSelectedRange(-1, -1);
                    }
                }
            }
            m_HoldPaint = false;
        }

        private void ClearSelectedRegionsInternal()
        {
            m_HoldPaint = true;
            foreach (ChartDataSeries dataSerie in MainChart.DataSeries)
            {
                if (dataSerie != m_MainDataSerie)
                {
                    dataSerie.ClearSelectedRegions();
                }
            }
            m_HoldPaint = false;
        }

        private void SetSelectedRangeInternal(ValueRange<DateTime> range)
        {
            ValueRange<float> remappedRange = RemapRangeToXAxis(range);

            SetSelectedRangeInternal(remappedRange);
        }

        private void SetSelectedRangeInternal(ValueRange<float> range)
        {
            m_HoldPaint = true;
            MainChart.PaintActive = false;
            {
                foreach (ChartDataSeries dataSerie in MainChart.DataSeries)
                {
                    // Only select our selected main chart & extra charts
                    if (dataSerie == m_SelectedDataSerie ||
                        (dataSerie != m_MainDataSerie &&
                         dataSerie != m_MainDataSerieCopy))
                    {
                        dataSerie.SetSelectedRange(range.Lower, range.Upper);
                    }
                }
            }
            MainChart.PaintActive = true;
            m_HoldPaint = false;
        }

        public void RefreshSelection()
        {
            MainChart.PaintActive = false;
            {
                if (OverlayMode)
                {
                    if (MainChart.DataSeries.Count > 0)
                    {
                        Debug.Assert(m_ValidRanges.Count == MainChart.DataSeries.Count);

                        float currentColorIndex = 0;
                        int segmentIndex = 0;

                        foreach (ChartDataSeries dataSerie in MainChart.DataSeries)
                        {
                            ValueRange<DateTime> currentSegment = m_ValidRanges[segmentIndex];

                            if (m_Selection.Contains(currentSegment))
                            {
                                const int SelectionLineWidth = 3;

                                if (dataSerie.LineWidth != SelectionLineWidth)
                                {
                                    dataSerie.LineColor = m_PresetOverlayColors[(int)currentColorIndex % m_PresetOverlayColors.Count];
                                    dataSerie.LineWidth = SelectionLineWidth;
                                }
                            }
                            else
                            {
                                const int UnselectedLineWidth = 2;

                                if (dataSerie.LineWidth != UnselectedLineWidth)
                                {
                                    dataSerie.LineColor = Color.FromArgb(102, ChartValidRegionColor);
                                    dataSerie.LineWidth = 2;
                                }
                            }

                            currentColorIndex += (float)Math.Max(1, (float)m_PresetOverlayColors.Count / m_ValidRanges.Count);
                            ++segmentIndex;
                        }
                    }
                }
                else
                {
                    ClearValidRangesInternal();
                    ClearSelectedRegionsInternal();
                    ClearSelectedRangeInternal();

                    // Valid ranges
                    foreach (ValueRange<DateTime> range in m_ValidRanges)
                    {
                        AddValidRangeInternal(range);
                    }

                    // Get the selected ranges
                    if (m_Selection.Count == 1)
                    {
                        SetSelectedRangeInternal(m_Selection[0]);
                    }
                    else
                    {
                        foreach (ValueRange<DateTime> range in m_Selection)
                        {
                            AddSelectedRangeInternal(range);
                        }
                    }
                }
            }
            MainChart.PaintActive = true;
        }

        public ValueRange<DateTime> GetRangeForDataSerie(ChartDataSeries dataSerie)
        {
            if (OverlayMode)
            {
                int validRangeIndex = 0;

                foreach (ChartDataSeries overlaySerie in MainChart.DataSeries)
                {
                    if (dataSerie == overlaySerie)
                    {
                        return m_ValidRanges[validRangeIndex];
                    }

                    ++validRangeIndex;
                }
            }
            else
            {
                Debug.Assert(false);

                // Main chart
                if (dataSerie == m_MainDataSerie ||
                    dataSerie == m_MainDataSerieCopy ||
                    dataSerie == m_SelectedDataSerie)
                {
                    INumericTimeDataSeries mainDataTrack = GetSmoothedActivityTrack(YAxisReferential);

                    return new ValueRange<DateTime>(mainDataTrack.StartTime,
                                                    mainDataTrack.StartTime + new TimeSpan(0, 0, (int)mainDataTrack.TotalElapsedSeconds));
                }
                else
                {
                    // Check for extra charts
                    foreach(ChartDataSeries currentDataSerie in MainChart.DataSeries)
                    {
                        if(dataSerie == currentDataSerie)
                        {
                            int extraChartIndex = 0;

                            foreach(IAxis extraAxis in MainChart.YAxisRight)
                            {
                                if(extraAxis == dataSerie.ValueAxis)
                                {
                                    IActivityDataTrackProvider provider = m_ExtraChartProviders[extraChartIndex];
                                    INumericTimeDataSeries extraDataTrack = provider.SmoothedDataTrack;
                                    
                                    return new ValueRange<DateTime>(extraDataTrack.StartTime,
                                                                    extraDataTrack.StartTime + new TimeSpan(0, 0, (int)extraDataTrack.TotalElapsedSeconds));
                                }

                                ++extraChartIndex;
                            }
                        }
                    }
                }
            }

            return null;
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IActivity Activity
        {
            get { return m_Activity; }
            set
            {
                if (m_Activity != value)
                {
                    m_Activity = value;
                    m_ActivityCallbackHelper.Activity = Activity;
                }

                if (Activity != null)
                {
                    m_ActivityInfoCache = ActivityInfoCache.Instance.GetInfo(Activity);
                }
                else
                {
                    m_ActivityInfoCache = null;
                }

                // We must always do this since we might have changed something in the activity
                m_ResetAxes = true;
                m_ResetAxesStripes = true;
                m_ResetDataSeries = true;

                if (ActivityDetailExtension.Instance.ActivityDetailPage.Visible)
                {
                    Invalidate();
                }
            }
        }

        public bool OverlayMode
        {
            get { return m_OverlayMode; }
            set
            {
                if(m_OverlayMode != value)
                {
                    m_OverlayMode = value;

                    m_ResetDataSeries = true;
                    m_ResetAxes = true;

                    ExtraChartsButton.Enabled = !OverlayMode;

                    if (ActivityDetailExtension.Instance.ActivityDetailPage != null &&
                        ActivityDetailExtension.Instance.ActivityDetailPage.Visible)
                    {
                        Invalidate();
                    }
                }
            }
        }

        [DisplayName("X Axis value")]
        public XAxisValue XAxisReferential
        {
            get { return m_XAxisReferential; }
            set
            {
                m_XAxisReferential = value;

                m_ResetAxes = true;
                m_ResetDataSeries = true;

                if (ActivityDetailExtension.Instance.ActivityDetailPage != null &&
                    ActivityDetailExtension.Instance.ActivityDetailPage.Visible)
                {
                    Invalidate();
                }
            }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Guid YAxisReferential
        {
            get
            {
                if (m_YAxisReferential != null)
                {
                    return m_YAxisReferential.ReferenceId;
                }
                else
                {
                    return Guid.Empty;
                }
            }
            set
            {
                if (DesignMode)
                {
                    return;
                }

                if (m_YAxisReferential == null || m_YAxisReferential.ReferenceId != value)
                {
                    if (m_YAxisReferential != null)
                    {
                        m_YAxisReferential.DataTrackChanged -= new PropertyChangedEventHandler(OnProviderDataTrackChanged);
                    }

                    m_YAxisReferential = ActivityDataTrackController.Instance.GetProvider(value);

                    if (m_YAxisReferential == null)
                    {
                        m_YAxisReferential = ActivityDataTrackController.Instance.GetDefaultProvider();
                    }

                    ShowZonesButton.Enabled = m_YAxisReferential != null &&
                                              m_YAxisReferential.DataTrackZoneGuids != null &&
                                              m_YAxisReferential.DataTrackZoneGuids.Count > 0;

                    // Refresh our default zone for this referential
                    m_ResetAxes = true;
                    m_ResetAxesStripes = true;
                    m_ResetDataSeries = true;

                    if (m_YAxisReferential != null)
                    {
                        m_YAxisReferential.DataTrackChanged += new PropertyChangedEventHandler(OnProviderDataTrackChanged);
                    }

                    if (ActivityDetailExtension.Instance.ActivityDetailPage != null &&
                        ActivityDetailExtension.Instance.ActivityDetailPage.Visible)
                    {
                        Invalidate();
                    }
                }
            }
        }

        [DisplayName("Allow Selection")]
        public bool AllowSelection
        {
            get { return MainChart.AllowSelection; }
            set { MainChart.AllowSelection = value; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ValueRange<float> SelectionReference
        {
            get
            {
                if (m_Selection.Count > 0)
                {
                    return RemapRangeToXAxis(m_Selection[0]);
                }

                return null;
            }
        }

        public Color ChartFillColor
        {
            get { return m_ChartFillColor; }
            set
            {
                if (m_ChartFillColor != value)
                {
                    m_ChartFillColor = value;

                    m_HoldPaint = true;
                    foreach (ChartDataSeries dataSerie in MainChart.DataSeries)
                    {
                        if (dataSerie != m_SelectedDataSerie)
                        {
                            dataSerie.FillColor = ChartFillColor;
                        }
                    }
                    m_HoldPaint = false;
                }
            }
        }

        public Color ChartLineColor
        {
            get { return m_ChartLineColor; }
            set
            {
                if (ChartLineColor != value)
                {
                    m_ChartLineColor = value;

                    m_HoldPaint = true;
                    foreach (ChartDataSeries dataSerie in MainChart.DataSeries)
                    {
                        if (dataSerie != m_SelectedDataSerie)
                        {
                            dataSerie.LineColor = ChartLineColor;
                        }
                    }
                    m_HoldPaint = false;
                }
            }
        }

        public Color ChartSelectedColor
        {
            get { return m_ChartSelectedColor; }
            set
            {
                if (ChartSelectedColor != value)
                {
                    m_ChartSelectedColor = value;

                    m_HoldPaint = true;
                    foreach (ChartDataSeries dataSerie in MainChart.DataSeries)
                    {
                        if (dataSerie != m_MainDataSerie)
                        {
                            dataSerie.SelectedColor = ChartSelectedColor;
                        }
                    }
                    m_HoldPaint = false;
                }
            }
        }

        public Color ChartValidRegionColor
        {
            get { return m_ChartValidRegionColor; }
            set
            {
                if (ChartValidRegionColor != value)
                {
                    m_ChartValidRegionColor = value;

                    m_HoldPaint = true;
                    if (m_MainDataSerie != null)
                    {
                        m_MainDataSerie.SelectedColor = ChartValidRegionColor;
                    }
                    m_HoldPaint = false;
                }
            }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Guid ShownZones
        {
            get { return m_ShownZones; }
            set
            {
                if (m_ShownZones != value)
                {
                    m_ShownZones = value;

                    m_HoldPaint = true;
                    if (MainChart != null && MainChart.YAxis != null)
                    {
                        SetupAxisStripes(MainChart.YAxis, YAxisReferential);
                    }
                    m_HoldPaint = false;
                }
            }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool PaintActive
        {
            get { return MainChart.PaintActive; }
            set { MainChart.PaintActive = value; }
        }

        [Category("Action")]
        public event ExtendedLineChart.ExtendedLineChartClickEventHandler ChartClick;
        [Category("Action")]
        public event ExtendedLineChart.ExtendedLineChartDataSerieClickEventHandler DataSerieClick;

        private IActivity m_Activity;
        private ActivityDataChangedHelper m_ActivityCallbackHelper = null;
        private ActivityInfo m_ActivityInfoCache;
        private XAxisValue m_XAxisReferential = XAxisValue.Time;
        private IActivityDataTrackProvider m_YAxisReferential = null;
        private List<IActivityDataTrackProvider> m_ExtraChartProviders = new List<IActivityDataTrackProvider>();
        private List<ValueRange<DateTime>> m_Selection = new List<ValueRange<DateTime>>();
        private List<ValueRange<DateTime>> m_ValidRanges = new List<ValueRange<DateTime>>();
        private List<Color> m_PresetOverlayColors = new List<Color>();
        private ChartDataSeries m_MainDataSerie = null;
        private ChartDataSeries m_MainDataSerieCopy = null;
        private ChartDataSeries m_SelectedDataSerie = null;
        private Color m_ChartFillColor = Color.WhiteSmoke;
        private Color m_ChartLineColor = Color.LightSkyBlue;
        private Color m_ChartValidRegionColor = Color.AliceBlue;
        private Color m_ChartSelectedColor = Color.DodgerBlue;
        private Guid m_ShownZones = Guid.Empty;
        private bool m_HoldPaint = false;
        private bool m_ResetAxes = false;
        private bool m_ResetAxesStripes = false;
        private bool m_ResetDataSeries = false;
        private bool m_OverlayMode = false;
    }
}
